/*******************************************************************************
 * Copyright (c) 2011 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl-3.0.txt
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legsem.legstar.connector.it.jee5.ejb;

import javax.annotation.Resource;
import javax.ejb.Local;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionFactory;

import com.legsem.legstar.cci.LegStarInteraction;
import com.legsem.legstar.cci.LegStarSingleIOInteractionSpec;
import com.legsem.legstar.cci.LegStarSingleIOInteractionSpecImpl;
import com.legstar.host.invoke.model.HostProgram;
import com.legstar.test.coxb.lsfileae.Dfhcommarea;
import com.legstar.test.coxb.lsfileae.bind.DfhcommareaTransformers;

/**
 * An EJB bean using the LegStar JCA Connector over Sockets.
 * <p/>
 * ClientBean updates to the mainframe are part of a container-managed
 * transaction.
 * <p/>
 * There are 3 CICS programs involved, all using the same commarea structure:
 * <ul>
 * <li>LSFILEAW: Creates a client record</li>
 * <li>LSFILEAE: Retrieves client data using a client number</li>
 * <li>LSFILEAR: Removes a client record</li>
 * </ul>
 * 
 */
@Stateless
@Local(Client.class)
public class ClientBean implements Client {

    /**
     * The JCA connector as known to JNDI.
     */
    @Resource(name = "legstar.socket.connector")
    private ConnectionFactory connectionFactory;

    /**
     * The session context to request transaction rollback when needed.
     */
    @Resource
    private SessionContext sctx;

    /**
     * Creates a new client. If the client number already exist, an exception is
     * raised and the transaction is rolled back.
     * 
     * @param commarea the client data
     * @return the input data
     * @throws ClientBeanException if client already exist
     */
    public Dfhcommarea createClient(Dfhcommarea commarea)
            throws ClientBeanException {
        return execute("LSFILEAW", commarea);
    }

    /**
     * Retrieves a client.
     * 
     * @param commarea the input client data
     * @return the requested client data
     * @throws ClientBeanException if client does not exist
     */
    public Dfhcommarea readClient(Dfhcommarea commarea)
            throws ClientBeanException {
        return execute("LSFILEAE", commarea);
    }

    /**
     * Generates a failure during an update process.
     * <p/>
     * The first write succeeds but the second fails (because the client is
     * already created). Since both updates are part of the same transaction,
     * roll back should undo both creations.
     * 
     * @param commarea the input client data
     * @return the input client data
     * @throws ClientBeanException when second write fails
     */
    public Dfhcommarea failCreateClient(Dfhcommarea commarea)
            throws ClientBeanException {
        execute("LSFILEAW", commarea);
        return execute("LSFILEAW", commarea);
    }

    /**
     * Removes a client.
     * 
     * @param commarea the input client data
     * @return the input client data
     * @throws ClientBeanException if client does not exist
     */
    public Dfhcommarea deleteClient(Dfhcommarea commarea)
            throws ClientBeanException {
        return execute("LSFILEAR", commarea);
    }

    /**
     * Generic execution of a remote program.
     * <p/>
     * Mainframe programs reply by setting the client number to zero if
     * something went wrong.
     * 
     * @param programName the program name
     * @param commarea the input structure
     * @return the output structure
     * @throws ClientBeanException if execution fails
     */
    private Dfhcommarea execute(String programName, Dfhcommarea commarea)
            throws ClientBeanException {
        Connection connection = null;
        try {
            connection = connectionFactory.getConnection();
            LegStarInteraction interaction = (LegStarInteraction) connection
                    .createInteraction();

            commarea = (Dfhcommarea) interaction.execute(getISpec(programName),
                    commarea);

            if (commarea.getComNumber() == 0) {
                sctx.setRollbackOnly();
                throw new ClientBeanException("Remote execution failed");
            }
            return commarea;

        } catch (ResourceException e) {
            sctx.setRollbackOnly();
            throw new ClientBeanException(e);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (ResourceException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Produce an interaction specification with a single input and output
     * structure.
     * <p/>
     * Besides the target mainframe program characteristics, the interaction
     * spec also specifies the LegStar Transformers to be used for input anbd
     * output structures.
     * 
     * @param programName the target program name
     * @return the interaction spec
     */
    private LegStarSingleIOInteractionSpec getISpec(String programName) {
        LegStarSingleIOInteractionSpec ispec = new LegStarSingleIOInteractionSpecImpl();
        HostProgram hostProgram = new HostProgram();
        hostProgram.setName(programName);
        hostProgram.setMaxDataLength(79);
        ispec.setHostProgram(hostProgram);
        DfhcommareaTransformers tf = new DfhcommareaTransformers();
        ispec.setInputTransformer(tf.getJavaToHost());
        ispec.setInteractionId(programName + ":" + System.currentTimeMillis());
        ispec.setOutputTransformer(tf.getHostToJava());
        return ispec;

    }

}
